import logging
from functools import wraps
from typing import Callable, TypeVar, cast

from flask import Blueprint, current_app, request
from flask import jsonify

from airflow.patsnap.service import datasource_service
from airflow.exceptions import AirflowException
from airflow.models.idata_datasource import Datasource
from airflow.www.api import provide_user, exception_handle

log = logging.getLogger(__name__)

T = TypeVar("T", bound=Callable)  # pylint: disable=invalid-name


def requires_authentication(function: T):
    """Decorator for functions that require authentication"""

    @wraps(function)
    def decorated(*args, **kwargs):
        return current_app.api_auth.requires_authentication(function)(*args, **kwargs)

    return cast(T, decorated)


api_speech_datasource = Blueprint('api_speech_datasource', __name__)


# user route
@api_speech_datasource.route('/datasources/<int:id>', methods=['GET'])
@exception_handle
def get_datasource(id):
    datasource = Datasource.get_datasource(id)
    return jsonify(code=0, message='ok', data=datasource.to_json())


@api_speech_datasource.route('/datasources', methods=['GET'])
@exception_handle
def get_datasources():
    page = request.args.get('page', 0, type=int)
    project_id = request.values.get("project_id", type=str, default=None)
    datasource_type = request.values.get("type", type=int, default=None)
    result = datasource_service.get_datasources(page, project_id, datasource_type)
    return jsonify(code=0, message='ok', data=result)


@api_speech_datasource.route('/datasources', methods=['POST'])
@exception_handle
@provide_user
def add_datasource(oa_user_name):
    datasource = request.json
    datasource_type = datasource['type']
    name = datasource['name']
    ip = datasource['ip']
    port = datasource['port']
    user_name = datasource['user_name']
    password = datasource['password']
    projects = datasource['projects']

    if datasource_type is None or name is None or projects is None:
        log.error(
            "add_datasource params error datasource_type={%s},name={%s},project_ids={%s}",
            datasource_type, name, projects)
        return jsonify(code=400, message="add_projects params has None,", data=None)
    datasource_service.add_datasource(datasource_type, name, ip, port, user_name, password, projects, oa_user_name)
    return jsonify(code=0, message='ok', data=None)


@api_speech_datasource.route('/datasources/<int:id>', methods=['DELETE'])
@exception_handle
def delete_datasource(id):
    datasource_service.delete_datasource(id)
    return jsonify(code=0, message='ok', data=None)


@api_speech_datasource.route('/datasources', methods=['PUT'])
@exception_handle
@provide_user
def update_datasource(oa_user_name):
    datasource = request.json
    datasource_id = datasource['id']
    datasource_type = datasource['type']
    name = datasource['name']
    ip = datasource['ip']
    port = datasource['port']
    user_name = datasource['user_name']
    password = datasource['password']
    projects = datasource['projects']

    if datasource_id is None or datasource_type is None or name is None or projects is None:
        log.error(
            "update_datasource params error datasource_id={%s},datasource_type={%s},name={%s},project_ids={%s}",
            datasource_id, datasource_type, name, projects)
        return jsonify(code=400, message="update_datasource params has None,", data=None)
    datasource_service.update_datasource(datasource_id, datasource_type, name, ip, port, user_name, password, projects, oa_user_name)
    return jsonify(code=0, message='ok', data=None)


@api_speech_datasource.route('/datasources/type', methods=['GET'])
@exception_handle
def list_datasources_type():
    reader_options = [{'value': 1, 'label': 'KAFKA'}, {'value': 2, 'label': 'HIVE'},
                      {'value': 3, 'label': 'JDBC'}, {'value': 4, 'label': 'ES'},
                      {'value': 5, 'label': 'DORIS'}]
    return jsonify(code=0, message='ok', data=reader_options)